home *** CD-ROM | disk | FTP | other *** search
/ Scene 96 / Scene 96 International Edition (Zyklop Software) (Disc 2) (1997).iso / misc / coding / midas060 / src / midasstr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-26  |  30.7 KB  |  1,099 lines

  1. /*      midasstr.c
  2.  *
  3.  * MIDAS stream library
  4.  *
  5.  * $Id: midasstr.c,v 1.7 1997/01/26 23:31:44 jpaana Exp $
  6.  *
  7.  * Copyright 1996,1997 Housemarque Inc.
  8.  *
  9.  * This file is part of the MIDAS Sound System, and may only be
  10.  * used, modified and distributed under the terms of the MIDAS
  11.  * Sound System license, LICENSE.TXT. By continuing to use,
  12.  * modify or distribute this file you indicate that you have
  13.  * read the license and understand and accept it fully.
  14. */
  15.  
  16. #ifdef __LINUX__
  17. #include <string.h>
  18. #include <unistd.h>
  19. #include <pthread.h>
  20. #else
  21. #define WIN32_LEAN_AND_MEAN
  22. #include <windows.h>
  23. #include <string.h>                     /* FIXME - don't use C RTL */
  24. #include <process.h>
  25. #endif
  26.  
  27. #include "lang.h"
  28. #include "mtypes.h"
  29. #include "errors.h"
  30. #include "mmem.h"
  31. #include "sdevice.h"
  32. #include "file.h"
  33. #include "midasstr.h"
  34.  
  35.  
  36.  
  37. /* Maximum number of streams: (just for keeping track of all streams in
  38.    playback) */
  39. #define MAXSTREAMS 32
  40. /* 32 streams will probably bring any system to its knees, but we'll just
  41.    be safe */
  42.  
  43. static strStream *streams[MAXSTREAMS];  /* all streams currently playing */
  44. static SoundDevice *SD;                 /* Sound Device for streams */
  45.  
  46.  
  47.  
  48. /****************************************************************************\
  49. *       enum strFunctIDs
  50. *       ----------------
  51. * Description:  Function IDs for stream library functions
  52. \****************************************************************************/
  53.  
  54. enum strFunctIDs
  55. {
  56.     ID_strInit = ID_str,
  57.     ID_strClose,
  58.     ID_strPlayStreamFile,
  59.     ID_strPlayStreamPolling,
  60.     ID_strPlayStreamCallback,
  61.     ID_strStopStream,
  62.     ID_strFeedStreamData,
  63.     ID_strSetStreamVolume,
  64.     ID_strSetStreamPanning,
  65.     ID_strIsStreamFinished,
  66.     ID_strSetStreamRate
  67. };
  68.  
  69.  
  70.  
  71.  
  72. /****************************************************************************\
  73. *
  74. * Function:     int strInit(SoundDevice *SD)
  75. *
  76. * Description:  Initializes the stream library
  77. *
  78. * Input:        SoundDevice *SD         Pointer to the Sound Device that will
  79. *                                       be used for playing the streams
  80. *
  81. * Returns:      MIDAS error code
  82. *
  83. \****************************************************************************/
  84.  
  85. int CALLING strInit(SoundDevice *_SD)
  86. {
  87.     int         i;
  88.  
  89.     /* Remember the Sound Device: */
  90.     SD = _SD;
  91.  
  92.     /* No streams are being played: */
  93.     for ( i = 0; i < MAXSTREAMS; i++ )
  94.         streams[i] = NULL;
  95.  
  96.     return OK;
  97. }
  98.  
  99.  
  100.  
  101.  
  102. /****************************************************************************\
  103. *
  104. * Function:     int strClose(void)
  105. *
  106. * Description:  Uninitializes the stream library
  107. *
  108. * Returns:      MIDAS error code
  109. *
  110. \****************************************************************************/
  111.  
  112. int CALLING strClose(void)
  113. {
  114.     int         i, error;
  115.  
  116.     /* Check if there are any streams being played - if yes, stop them: */
  117.     for ( i = 0; i < MAXSTREAMS; i++ )
  118.     {
  119.         if ( streams[i] != NULL )
  120.         {
  121.             if ( (error = strStopStream(streams[i])) != OK )
  122.                 PASSERROR(ID_strClose)
  123.         }
  124.     }
  125.  
  126.     return OK;
  127. }
  128.  
  129.  
  130.  
  131. /****************************************************************************\
  132. *
  133. * Function:     static int StartStream(strStream **stream)
  134. *
  135. * Description:  Starts a new stream, and allocates the info structure.
  136. *
  137. * Input:        strStream **stream      pointer to steam state pointer
  138. *
  139. * Returns:      MIDAS error code
  140. *
  141. \****************************************************************************/
  142.  
  143. static int StartStream(strStream **stream)
  144. {
  145.     int         error;
  146.     int         num;
  147.  
  148.     /* Find a free spot from the stream table: */
  149.     num = 0;
  150.     while ( streams[num] != NULL )
  151.     {
  152.         num++;
  153.         if ( num >= MAXSTREAMS )
  154.             return errOutOfResources;
  155.     }
  156.  
  157.     /* Allocate memory for the stream state structure: */
  158.     if ( (error = memAlloc(sizeof(strStream), (void**) stream)) != OK )
  159.         return error;
  160.  
  161.     streams[num] = *stream;
  162.  
  163.     return OK;
  164. }
  165.  
  166.  
  167.  
  168.  
  169. /****************************************************************************\
  170. *
  171. * Function:     static int FreeStream(strStream *stream)
  172. *
  173. * Description:  Deallocates a steram and removes it from the stream list
  174. *
  175. * Input:        strStream *stream       stream to be deallocated
  176. *
  177. * Returns:      MIDAS error code
  178. *
  179. \****************************************************************************/
  180.  
  181. static int FreeStream(strStream *stream)
  182. {
  183.     int         error;
  184.     int         i;
  185.  
  186.     /* Remove the stream from the stream list: */
  187.     for ( i = 0; i < MAXSTREAMS; i++ )
  188.     {
  189.         if ( streams[i] == stream )
  190.             streams[i] = NULL;
  191.     }
  192.  
  193.     /* Deallocate the stream structure: */
  194.     if ( (error = memFree(stream)) != OK )
  195.         return error;
  196.  
  197.     return OK;
  198. }
  199.  
  200.  
  201.  
  202. /****************************************************************************\
  203. *
  204. * Function:     unsigned StreamBufferLeft(strStream *stream)
  205. *
  206. * Description:  Calculates the number of bytes of free space in a stream
  207. *               buffer
  208. *
  209. * Input:        strStream *stream       stream to check
  210. *
  211. * Returns:      Number of bytes of free space left
  212. *
  213. \****************************************************************************/
  214.  
  215. static unsigned StreamBufferLeft(strStream *stream)
  216. {
  217.     unsigned    readPos;
  218.     unsigned    spaceLeft;
  219.  
  220.     /* FIXME - error callback? */
  221.  
  222.     /* Get reading position: */
  223.     SD->GetPosition(stream->sdChannel, &readPos);
  224.  
  225.     /* If read and write positions are equal, the whole buffer is empty: */
  226.     if ( readPos == stream->bufferWritePos )
  227.         return stream->bufferBytes - stream->sampleSize;
  228.  
  229.     /* Calculate the amount of free space: */
  230.     if ( readPos >= stream->bufferWritePos )
  231.         spaceLeft = readPos - stream->bufferWritePos;
  232.     else
  233.         spaceLeft = stream->bufferBytes - stream->bufferWritePos + readPos;
  234.  
  235.     /* Make sure that we won't wrap around: */
  236.     if ( spaceLeft >= stream->sampleSize )
  237.         spaceLeft -= stream->sampleSize;
  238.     else
  239.         spaceLeft = 0;
  240.  
  241.     return spaceLeft;
  242. }
  243.  
  244.  
  245.  
  246.  
  247. /****************************************************************************\
  248. *
  249. * Function:     void WriteStreamData(strStream *stream, uchar *data,
  250. *                   unsigned numBytes)
  251. *
  252. * Description:  Writes data to a stream buffer, updating buffer write position
  253. *               and taking care of wraparound
  254. *
  255. * Input:        strStream *stream       stream for the data
  256. *               uchar *data             pointer to data
  257. *               unsigned numBytes       number of bytes of data to write
  258. *
  259. \****************************************************************************/
  260.  
  261. static void WriteStreamData(strStream *stream, uchar *data, unsigned numBytes)
  262. {
  263.     unsigned    len = stream->bufferBytes;
  264.     unsigned    pos = stream->bufferWritePos;
  265.     unsigned    left, now;
  266.  
  267.     /* Loop until we get everything copied: */
  268.     while ( numBytes )
  269.     {
  270.         /* Get number of bytes of space before buffer end: */
  271.         left = len - pos;
  272.  
  273.         /* Don't copy past buffer end: */
  274.         if ( numBytes > left )
  275.             now = left;
  276.         else
  277.             now = numBytes;
  278.  
  279.         /* Copy the data: */
  280.         memcpy(&stream->buffer[pos], data, now);
  281.  
  282.         /* Advance buffer position: */
  283.         pos += now;
  284.         numBytes -= now;
  285.         data += now;
  286.  
  287.         /* Wrap to buffer beginning if necessary: */
  288.         if ( pos >= len )
  289.             pos = 0;
  290.     }
  291.  
  292.     /* Remember the new buffer position: */
  293.     stream->bufferWritePos = pos;
  294.     /* FIXME: error check: */
  295.     SD->SetStreamWritePosition(stream->sdChannel, pos);
  296. }
  297.  
  298.  
  299.  
  300.  
  301. /****************************************************************************\
  302. *
  303. * Function:     void FillStreamBuffer(strStream *stream, uchar value,
  304. *                   unsigned numBytes)
  305. *
  306. * Description:  Fills a stream buffer with a byte, updating buffer write
  307. *               position and taking care of wraparound
  308. *
  309. * Input:        strStream *stream       stream for the data
  310. *               uchar value             byte to fill the buffer with
  311. *               unsigned numBytes       number of bytes to fill
  312. *
  313. \****************************************************************************/
  314.  
  315. static void FillStreamBuffer(strStream *stream, uchar value,
  316.     unsigned numBytes)
  317. {
  318.     unsigned    len = stream->bufferBytes;
  319.     unsigned    pos = stream->bufferWritePos;
  320.     unsigned    left, now;
  321.  
  322.     /* Loop until we get everything filled */
  323.     while ( numBytes )
  324.     {
  325.         /* Get number of bytes of space before buffer end: */
  326.         left = len - pos;
  327.  
  328.         /* Don't fill past buffer end: */
  329.         if ( numBytes > left )
  330.             now = left;
  331.         else
  332.             now = numBytes;
  333.  
  334.         /* Fill it: */
  335.         memset(&stream->buffer[pos], value, now);
  336.  
  337.         /* Advance buffer position: */
  338.         pos += now;
  339.         numBytes -= now;
  340.  
  341.         /* Wrap to buffer beginning if necessary: */
  342.         if ( pos >= len )
  343.             pos = 0;
  344.     }
  345.  
  346.     /* Remember the new buffer position: */
  347.     stream->bufferWritePos = pos;
  348.     /* FIXME: error check: */
  349.     SD->SetStreamWritePosition(stream->sdChannel, pos);
  350. }
  351.  
  352.  
  353.  
  354.  
  355. /****************************************************************************\
  356. *
  357. * Function:     void StreamPlayerThread(void *stream)
  358. *
  359. * Description:  The stream player thread
  360. *
  361. * Input:        void *stream            stream state information for this
  362. *                                       stream
  363. *
  364. \****************************************************************************/
  365.  
  366. #ifdef __LINUX__
  367. static void* StreamPlayerThread(void *stream)
  368. #else
  369. #ifdef __WC32__
  370. static void StreamPlayerThread(void *stream)
  371. #else
  372. static void __cdecl StreamPlayerThread(void *stream)
  373. #endif
  374. #endif
  375. {
  376.     /* FIXME - we should check for errors, but for that we need some way to
  377.        report the errors to the user - a callback maybe? */
  378.  
  379.     unsigned    bufferLeft;
  380.     unsigned    doNow;
  381.     strStream   *s = (strStream*) stream;
  382.  
  383.     /* Round and round we go, until it's exit time */
  384.  
  385.     while ( !s->threadExitFlag )
  386.     {
  387.         /* Get amount of free space in buffer: */
  388.         bufferLeft = StreamBufferLeft(s);
  389.  
  390.         /* Fill the free space - either read data from the file or just
  391.            clear it: */
  392.         while ( bufferLeft )
  393.         {
  394.             if ( s->fileLeft )
  395.             {
  396.                 /* There is still data in the file to read */
  397.  
  398.                 /* Don't read past End Of File: */
  399.                 if ( bufferLeft > s->fileLeft )
  400.                     doNow = s->fileLeft;
  401.                 else
  402.                     doNow = bufferLeft;
  403.  
  404.                 /* Read the data from the file: */
  405.                 /* (the file buffer is always at least as large as the
  406.                    stream buffer so we can't overflow) */
  407.                 fileRead(s->f, s->fileBuffer, doNow);
  408.  
  409.                 /* Write the data to the stream buffer: */
  410.                 WriteStreamData(s, s->fileBuffer, doNow);
  411.  
  412.                 bufferLeft -= doNow;
  413.                 s->fileLeft -= doNow;
  414.  
  415.                 /* Check if we reached the file end and should loop: */
  416.                 if ( (s->fileLeft <= 0) && (s->loop) )
  417.                 {
  418.                     /* Restart the file: */
  419.                     s->fileLeft = s->fileLength;
  420.                     fileSeek(s->f, 0, fileSeekAbsolute);
  421.                 }
  422.             }
  423.             else
  424.             {
  425.                 /* No data left in file, just clear the buffer: */
  426.                 FillStreamBuffer(s, s->bufferClearVal, bufferLeft);
  427.                 bufferLeft = 0;
  428.             }
  429.         }
  430.  
  431.         /* Sleep for a while: */
  432. #ifdef __LINUX__
  433.         usleep(s->threadDelay * 1000);
  434. #else
  435.         Sleep(s->threadDelay);
  436. #endif
  437.     }
  438.  
  439.     /* Exit flag detected: */
  440.     s->threadExitFlag = 0;
  441.  
  442.     /* Get lost: */
  443. #ifdef __LINUX__
  444.     pthread_exit(0);
  445.     return NULL;
  446. #else
  447.     _endthread();
  448. #endif
  449. }
  450.  
  451.  
  452.  
  453.  
  454. /****************************************************************************\
  455. *
  456. * Function:     int strPlayStreamFile(unsigned channel, char *fileName,
  457. *                   unsigned sampleType, ulong sampleRate,
  458. *                   unsigned bufferLength, int loop, strStream **stream)
  459. *
  460. * Description:  Starts playing a digital sound stream from a file. Creates a
  461. *               new thread that will take care of reading the file and feeding
  462. *               it to the stream buffer
  463. *
  464. * Input:        unsigned channel        channel number for the stream
  465. *               char *fileName          stream file name
  466. *               unsigned sampleType     stream sample type
  467. *               ulong sampleRate        sampling rate
  468. *               unsigned bufferLength   stream buffer length in milliseconds
  469. *               int loop                1 if the stream should be looped,
  470. *                                       0 if not
  471. *               strStream **stream      pointer to stream state pointer
  472. *
  473. * Returns:      MIDAS error code. Pointer to the stream state structure will
  474. *               be written to *stream
  475. *
  476. \****************************************************************************/
  477.  
  478. int CALLING strPlayStreamFile(unsigned channel, char *fileName,
  479.     unsigned sampleType, ulong sampleRate, unsigned bufferLength, int loop,
  480.     strStream **stream)
  481. {
  482.     int         error;
  483.     long        fileLen;
  484.     fileHandle  f;
  485.     strStream   *s;
  486. #ifdef __WIN32__
  487. #ifdef __WC32__
  488.     int         streamThread;
  489. #else
  490.     ulong       streamThread;
  491. #endif
  492. #endif
  493. #ifdef __LINUX__
  494.     int         code;
  495. #endif
  496.  
  497.  
  498.     /* Stop any sound on the channel: */
  499.     if ( (error = SD->StopSound(channel)) != OK )
  500.         PASSERROR(ID_strPlayStreamFile);
  501.  
  502.     /* Allocate a stream state structure for the new stream: */
  503.     if ( (error = StartStream(stream)) != OK )
  504.         PASSERROR(ID_strPlayStreamFile);
  505.     s = *stream;
  506.  
  507.     /* Initialize the stream structure: */
  508.     s->sdChannel = channel;
  509.     s->streamMode = strStreamPlayFile;
  510.     s->loop = loop;
  511.     s->threadExitFlag = 0;
  512.     s->threadDelay = bufferLength / 8;
  513.     s->callback = NULL;
  514.  
  515.     /* Check the sample type and initialize accordingly: */
  516.     switch ( sampleType )
  517.     {
  518.         case smp8bitMono:
  519.             s->sampleSize = 1;
  520.             s->bufferClearVal = 128;
  521.             break;
  522.  
  523.         case smp16bitMono:
  524.             s->sampleSize = 2;
  525.             s->bufferClearVal = 0;
  526.             break;
  527.  
  528.         case smp8bitStereo:
  529.             s->sampleSize = 2;
  530.             s->bufferClearVal = 128;
  531.             break;
  532.  
  533.         case smp16bitStereo:
  534.             s->sampleSize = 4;
  535.             s->bufferClearVal = 0;
  536.             break;
  537.  
  538.         default:
  539.             ERROR(errInvalidArguments, ID_strPlayStreamFile);
  540.             return errInvalidArguments;
  541.     }
  542.  
  543.     /* Calculate the buffer size in samples: (make it a multiple of 4) */
  544.     s->bufferSamples = ((bufferLength * sampleRate / 1000) + 3) & (~3);
  545.  
  546.     /* And in bytes: */
  547.     s->bufferBytes = s->sampleSize * s->bufferSamples;
  548.  
  549.     /* Allocate the buffer: */
  550.     if ( (error = memAlloc(s->bufferBytes, (void**) &s->buffer)) != OK )
  551.     {
  552.         FreeStream(s);
  553.         PASSERROR(ID_strPlayStreamFile);
  554.     }
  555.  
  556.     /* Clear it: */
  557.     s->bufferWritePos = 0;
  558.     memset(s->buffer, s->bufferClearVal, s->bufferBytes);
  559.  
  560.     /* Allocate a file reading buffer: */
  561.     s->fileBufferBytes = s->bufferBytes;
  562.     if ( (error = memAlloc(s->fileBufferBytes, (void**) &s->fileBuffer))
  563.         != OK )
  564.     {
  565.         memFree(s->buffer);
  566.         FreeStream(s);
  567.         PASSERROR(ID_strPlayStreamFile);
  568.     }
  569.  
  570.     /* Open the stream file and get its length: */
  571.     if ( (error = fileOpen(fileName, fileOpenRead, &f)) != OK )
  572.     {
  573.         memFree(s->fileBuffer);
  574.         memFree(s->buffer);
  575.         FreeStream(s);
  576.         PASSERROR(ID_strPlayStreamFile);
  577.     }
  578.  
  579.     if ( (error = fileGetSize(f, &fileLen)) != OK )
  580.     {
  581.         fileClose(f);
  582.         memFree(s->fileBuffer);
  583.         memFree(s->buffer);
  584.         FreeStream(s);
  585.         PASSERROR(ID_strPlayStreamFile);
  586.     }
  587.  
  588.     s->f = f;
  589.     s->fileLength = s->fileLeft = fileLen;
  590.  
  591.     /* If the file is larger than the stream buffer, fill the buffer with data
  592.        from the file to minimize delay at startup: */
  593.     if ( s->fileLeft > s->bufferBytes )
  594.     {
  595.         if ( (error = fileRead(f, s->buffer, s->bufferBytes -
  596.             s->sampleSize)) != OK )
  597.         {
  598.             fileClose(f);
  599.             memFree(s->fileBuffer);
  600.             memFree(s->buffer);
  601.             FreeStream(s);
  602.             PASSERROR(ID_strPlayStreamFile);
  603.         }
  604.         s->fileLeft -= s->bufferBytes - s->sampleSize;
  605.  
  606.         s->bufferWritePos = s->bufferBytes - s->sampleSize;
  607.     }
  608.  
  609.     /* Start playing the stream: */
  610.     if ( (error = SD->StartStream(s->sdChannel, s->buffer, s->bufferBytes,
  611.         sampleType, sampleRate)) != OK )
  612.     {
  613.         fileClose(f);
  614.         memFree(s->fileBuffer);
  615.         memFree(s->buffer);
  616.         FreeStream(s);
  617.         PASSERROR(ID_strPlayStreamFile);
  618.     }
  619.  
  620.     /* Set stream write position to the end of (possibly) just read data: */
  621.     if ( (error = SD->SetStreamWritePosition(s->sdChannel, s->bufferWritePos))
  622.         != OK )
  623.     {
  624.         SD->StopStream(s->sdChannel);
  625.         fileClose(f);
  626.         memFree(s->fileBuffer);
  627.         memFree(s->buffer);
  628.         FreeStream(s);
  629.         PASSERROR(ID_strPlayStreamFile);
  630.     }
  631.  
  632.     /* Start the stream player thread: */
  633.  
  634. #ifdef __WIN32__
  635. #ifdef __WC32__
  636.     streamThread = _beginthread(StreamPlayerThread, NULL, 4096, (void*) s);
  637.  
  638.     if ( streamThread == -1 )
  639.     {
  640.         /* Couldn't create thread */
  641.         SD->StopStream(s->sdChannel);
  642.         fileClose(f);
  643.         memFree(s->fileBuffer);
  644.         memFree(s->buffer);
  645.         FreeStream(s);
  646.         ERROR(errOutOfResources, ID_strPlayStreamFile);
  647.         return errOutOfResources;
  648.     }
  649. #else
  650.     streamThread = _beginthread(StreamPlayerThread, 4096, (void*) s);
  651.  
  652.     if ( streamThread == -1 )
  653.     {
  654.         /* Couldn't create thread */
  655.         SD->StopStream(s->sdChannel);
  656.         fileClose(f);
  657.         memFree(s->fileBuffer);
  658.         memFree(s->buffer);
  659.         FreeStream(s);
  660.         ERROR(errOutOfResources, ID_strPlayStreamFile);
  661.         return errOutOfResources;
  662.     }
  663. #endif
  664. #else
  665.     code = pthread_create(&s->playerThread, NULL, StreamPlayerThread,
  666.         (void*) s);
  667.     if ( code )
  668.     {
  669.         /* Couldn't create thread */
  670.         SD->StopStream(s->sdChannel);
  671.         fileClose(f);
  672.         memFree(s->fileBuffer);
  673.         memFree(s->buffer);
  674.         FreeStream(s);
  675.         ERROR(errOutOfResources, ID_strPlayStreamFile);
  676.         return errOutOfResources;
  677.     }
  678. #endif
  679.     /* The thread is playing OK */
  680.  
  681.     return OK;
  682. }
  683.  
  684.  
  685.  
  686.  
  687. /****************************************************************************\
  688. *
  689. * Function:     int strStopStream(strStream *stream)
  690. *
  691. * Description:  Stops playing a stream. This function will also destroy the
  692. *               playback thread for stream file playback.
  693. *
  694. * Input:        strStream *stream       stream state pointer for the stream
  695. *
  696. * Returns:      MIDAS error code.
  697. *
  698. \****************************************************************************/
  699.  
  700. int CALLING strStopStream(strStream *stream)
  701. {
  702. #ifdef __LINUX__
  703.     void        *retval;
  704. #endif
  705.     int         error;
  706.  
  707.     /* Stop the player thread if we are playing from a file: */
  708.     if ( stream->streamMode == strStreamPlayFile )
  709.     {
  710.         /* Signal the stream player thread that it should actually stop: */
  711.         stream->threadExitFlag = 1;
  712.  
  713.         /* Wait until it stops: */
  714. #ifdef __LINUX__
  715.         pthread_join(stream->playerThread, &retval);
  716. #else
  717.         while ( stream->threadExitFlag )
  718.             Sleep(stream->threadDelay);
  719. #endif
  720.         /* [Now is that ugly or what? But it works and it's portable] */
  721.     }
  722.  
  723.     /* Stop the Sound Device playing the stream: */
  724.     if ( (error = SD->StopStream(stream->sdChannel)) != OK )
  725.     {
  726.         /* Failed - let's try to do at least some cleanup anyway: */
  727.         if ( stream->streamMode == strStreamPlayFile )
  728.         {
  729.             fileClose(stream->f);
  730.             memFree(stream->fileBuffer);
  731.         }
  732.         memFree(stream->buffer);
  733.         FreeStream(stream);
  734.         PASSERROR(ID_strStopStream);
  735.     }
  736.  
  737.     /* Close the file and deallocate file buffer only if we were playing a
  738.        stream file: */
  739.     if ( stream->streamMode == strStreamPlayFile )
  740.     {
  741.         /* Close the stream file: */
  742.         if ( (error = fileClose(stream->f)) != OK )
  743.         {
  744.             memFree(stream->fileBuffer);
  745.             memFree(stream->buffer);
  746.             FreeStream(stream);
  747.             PASSERROR(ID_strStopStream);
  748.         }
  749.  
  750.         /* Deallocate stream file buffer: */
  751.         if ( (error = memFree(stream->fileBuffer)) != OK )
  752.         {
  753.             memFree(stream->buffer);
  754.             FreeStream(stream);
  755.             PASSERROR(ID_strStopStream);
  756.         }
  757.     }
  758.  
  759.     /* Deallocate stream buffer: */
  760.     if ( (error = memFree(stream->buffer)) != OK )
  761.     {
  762.         FreeStream(stream);
  763.         PASSERROR(ID_strStopStream);
  764.     }
  765.  
  766.     /* Finally, free the stream: */
  767.     if ( (error = FreeStream(stream)) != OK )
  768.         PASSERROR(ID_strStopStream);
  769.  
  770.     /* Phew, done: */
  771.     return OK;
  772. }
  773.  
  774.  
  775.  
  776.  
  777. /****************************************************************************\
  778. *
  779. * Function:     int strPlayStreamPolling(unsigned channel,
  780. *                   unsigned sampleType, ulong sampleRate,
  781. *                   unsigned bufferLength, strStream **stream)
  782. *
  783. * Description:  Starts playing a stream in polling mode. Use
  784. *               strFeedStreamData() to feed the stream data to the player
  785. *
  786. * Input:        unsigned channel        channel number for the stream
  787. *               unsigned sampleType     stream sample type
  788. *               ulong sampleRate        stream sampling rate
  789. *               unsigned bufferLength   stream buffer length in milliseconds
  790. *               strStream **stream      pointer to stream state pointer
  791. *
  792. * Returns:      MIDAS error code. Pointer to the stream state structure will
  793. *               be written to *stream
  794. *
  795. \****************************************************************************/
  796.  
  797. int CALLING strPlayStreamPolling(unsigned channel, unsigned sampleType,
  798.     ulong sampleRate, unsigned bufferLength, strStream **stream)
  799. {
  800.     int         error;
  801.     strStream   *s;
  802.  
  803.     /* Stop any sound on the channel: */
  804.     if ( (error = SD->StopSound(channel)) != OK )
  805.         PASSERROR(ID_strPlayStreamFile);
  806.  
  807.     /* Allocate a stream state structure for the new stream: */
  808.     if ( (error = StartStream(stream)) != OK )
  809.         PASSERROR(ID_strPlayStreamFile);
  810.     s = *stream;
  811.  
  812.     /* Initialize the stream structure: */
  813.     s->sdChannel = channel;
  814.     s->streamMode = strStreamPoll;
  815.     s->loop = 1;
  816.     s->threadExitFlag = 0;
  817.     s->threadDelay = bufferLength / 8;
  818.     s->callback = NULL;
  819.  
  820.     /* Check the sample type and initialize accordingly: */
  821.     switch ( sampleType )
  822.     {
  823.         case smp8bitMono:
  824.             s->sampleSize = 1;
  825.             s->bufferClearVal = 128;
  826.             break;
  827.  
  828.         case smp16bitMono:
  829.             s->sampleSize = 2;
  830.             s->bufferClearVal = 0;
  831.             break;
  832.  
  833.         case smp8bitStereo:
  834.             s->sampleSize = 2;
  835.             s->bufferClearVal = 128;
  836.             break;
  837.  
  838.         case smp16bitStereo:
  839.             s->sampleSize = 4;
  840.             s->bufferClearVal = 0;
  841.             break;
  842.  
  843.         default:
  844.             ERROR(errInvalidArguments, ID_strPlayStreamFile);
  845.             return errInvalidArguments;
  846.     }
  847.  
  848.     /* Calculate the buffer size in samples: (make it a multiple of 4) */
  849.     s->bufferSamples = ((bufferLength * sampleRate / 1000) + 3) & (~3);
  850.  
  851.     /* And in bytes: */
  852.     s->bufferBytes = s->sampleSize * s->bufferSamples;
  853.  
  854.     /* Allocate the buffer: */
  855.     if ( (error = memAlloc(s->bufferBytes, (void**) &s->buffer)) != OK )
  856.     {
  857.         FreeStream(s);
  858.         PASSERROR(ID_strPlayStreamPolling);
  859.     }
  860.  
  861.     /* Clear it: */
  862.     s->bufferWritePos = 0;
  863.     memset(s->buffer, s->bufferClearVal, s->bufferBytes);
  864.  
  865.     /* Start playing the stream: */
  866.     if ( (error = SD->StartStream(s->sdChannel, s->buffer, s->bufferBytes,
  867.         sampleType, sampleRate)) != OK )
  868.     {
  869.         memFree(s->buffer);
  870.         FreeStream(s);
  871.         PASSERROR(ID_strPlayStreamFile);
  872.     }
  873.  
  874.     return OK;
  875. }
  876.  
  877.  
  878.  
  879. /****************************************************************************\
  880. *
  881. * Function:     int strPlayStreamCallback(unsigned sampleType,
  882. *                  ulong sampleRate, unsigned bufferBytes,
  883. *                  void (CALLING *callback)(uchar *buffer, strStream *stream))
  884. *
  885. * Description:  Starts playing a stream with a callback.
  886. *
  887. * Input:        unsigned sampleType     stream sample type
  888. *               ulong sampleRate        stream sampling rate
  889. *               unsigned bufferBytes    stream buffer size _IN BYTES_
  890. *               ... *callback           stream player callback
  891. *               strStream **stream      pointer to stream state pointer
  892. *
  893. * Returns:      MIDAS error code. Pointer to the stream state structure will
  894. *               be written to *stream
  895. *
  896. * Notes:        The callback function will be called each time the whole
  897. *               stream buffer needs to be filled. It receives as an argument
  898. *               a pointer to the buffer, and the stream state pointer.
  899. *
  900. *               The function will be called from inside the mixing routine,
  901. *               so it should return relatively rapidly - do not use this
  902. *               function for, for example, loading data from disc.
  903. *
  904. \****************************************************************************/
  905.  
  906. int CALLING strPlayStreamCallback(unsigned sampleType, ulong sampleRate,
  907.     unsigned bufferBytes,
  908.     void (CALLING *callback)(uchar *buffer, strStream *stream));
  909.  
  910.  
  911.  
  912.  
  913. /****************************************************************************\
  914. *
  915. * Function:     int strFeedStreamData(strStream *stream, uchar *data,
  916. *                   unsigned numBytes, int feedAll, unsigned *numFed)
  917. *
  918. * Description:  Feeds sample data to a stream that is being played in polling
  919. *               mode.
  920. *
  921. * Input:        strStream *stream       stream state pointer from
  922. *                                       strPlayStreamPolling()
  923. *               uchar *data             pointer to stream data
  924. *               unsigned numBytes       number of bytes of data to feed. Note!
  925. *                                       This must be a multiple of the stream
  926. *                                       sample size
  927. *               int feedAll             1 if all data should be fed in all
  928. *                                       circumstances. The function will block
  929. *                                       the current thread if this flag is 1
  930. *                                       until all data is fed.
  931. *               unsigned *numFed        pointer to a variable that will
  932. *                                       contain the number of bytes actually
  933. *                                       fed
  934. *
  935. * Returns:      MIDAS error code. The number of bytes of data actually fed is
  936. *               written to *numFed.
  937. *
  938. \****************************************************************************/
  939.  
  940. int CALLING strFeedStreamData(strStream *stream, uchar *data,
  941.     unsigned numBytes, int feedAll, unsigned *numFed)
  942. {
  943.     unsigned    bufferLeft;
  944.     unsigned    doNow;
  945.  
  946.     *numFed = 0;
  947.  
  948.     /* Loop until all data has been fed if feedAll is 1: */
  949.     do
  950.     {
  951.         /* Get amount of free space in buffer: */
  952.         bufferLeft = StreamBufferLeft(stream);
  953.  
  954.         if ( bufferLeft > numBytes )
  955.             doNow = numBytes;
  956.         else
  957.             doNow = bufferLeft;
  958.  
  959.         /* Write the data there: */
  960.         WriteStreamData(stream, data, doNow);
  961.  
  962.         /* Update pointers and counters: */
  963.         numBytes -= doNow;
  964.         *numFed += doNow;
  965.         data += doNow;
  966.  
  967.         /* If we should feed all data, and there is data to go, sleep for
  968.            a while: */
  969.         if ( feedAll && numBytes )
  970.         {
  971. #ifdef __LINUX__
  972.             usleep(stream->threadDelay * 1000);
  973. #else
  974.             Sleep(stream->threadDelay);
  975. #endif
  976.         }
  977.     } while ( feedAll && numBytes );
  978.  
  979.     return OK;
  980. }
  981.  
  982.  
  983.  
  984. /****************************************************************************\
  985. *
  986. * Function:     int strSetStreamRate(strStream *stream, ulong sampleRate)
  987. *
  988. * Description:  Changes the sampling rate for a stream
  989. *
  990. * Input:        strStream *stream       stream state pointer
  991. *               ulong sampleRate        new sampling rate in Hz
  992. *
  993. * Returns:      MIDAS error code
  994. *
  995. \****************************************************************************/
  996.  
  997. int CALLING strSetStreamRate(strStream *stream, ulong sampleRate)
  998. {
  999.     int         error;
  1000.  
  1001.     /* Set the sample rate: */
  1002.     if ( (error = SD->SetRate(stream->sdChannel, sampleRate)) != OK )
  1003.         PASSERROR(ID_strSetStreamRate);
  1004.  
  1005.     return OK;
  1006. }
  1007.  
  1008.  
  1009.  
  1010.  
  1011. /****************************************************************************\
  1012. *
  1013. * Function:     int strSetStreamVolume(strStream *stream, unsigned volume)
  1014. *
  1015. * Description:  Changes the playback volume for a stream (the default is 64).
  1016. *
  1017. * Input:        strStream *stream       stream state pointer
  1018. *               unsigned volume         new volume (0-64)
  1019. *
  1020. * Returns:      MIDAS error code.
  1021. *
  1022. \****************************************************************************/
  1023.  
  1024. int CALLING strSetStreamVolume(strStream *stream, unsigned volume)
  1025. {
  1026.     int         error;
  1027.  
  1028.     /* Set the volume: */
  1029.     if ( (error = SD->SetRate(stream->sdChannel, volume)) != OK )
  1030.         PASSERROR(ID_strSetStreamVolume);
  1031.  
  1032.     return OK;
  1033. }
  1034.  
  1035.  
  1036.  
  1037.  
  1038. /****************************************************************************\
  1039. *
  1040. * Function:     int strSetStreamPanning(strStream *stream, int panning)
  1041. *
  1042. * Description:  Changes the panning for a stream (the default is middle).
  1043. *
  1044. * Input:        strStream *stream       stream state pointer
  1045. *               int panning             new panning position
  1046. *
  1047. * Returns:      MIDAS error code.
  1048. *
  1049. \****************************************************************************/
  1050.  
  1051. int CALLING strSetStreamPanning(strStream *stream, int panning)
  1052. {
  1053.     int         error;
  1054.  
  1055.     /* Set the panning position: */
  1056.     if ( (error = SD->SetRate(stream->sdChannel, panning)) != OK )
  1057.         PASSERROR(ID_strSetStreamPanning);
  1058.  
  1059.     return OK;
  1060. }
  1061.  
  1062.  
  1063.  
  1064.  
  1065. /****************************************************************************\
  1066. *
  1067. * Function:     int strIsStreamFinished(strStream *stream, int *finished)
  1068. *
  1069. * Description:  Checks whether a given stream has reached the end of the
  1070. *               stream file or not. Only applies to streams played from a
  1071. *               file.
  1072. *
  1073. * Input:        strStream *stream       stream state pointer
  1074. *               int *finished           pointer to result variable
  1075. *
  1076. * Returns:      MIDAS error code. If the stream is finished, 1 will be written
  1077. *               to *finished, otherwise *finished will contain 0.
  1078. *
  1079. \****************************************************************************/
  1080.  
  1081. int CALLING strIsStreamFinished(strStream *stream, int *finished);
  1082.  
  1083.  
  1084. /*
  1085.  * $Log: midasstr.c,v $
  1086.  * Revision 1.7  1997/01/26 23:31:44  jpaana
  1087.  * Small fixes for pthreads
  1088.  *
  1089.  * Revision 1.6  1997/01/16 19:31:53  pekangas
  1090.  * Fixed to compile with Linux GCC (but do they work?)
  1091.  *
  1092.  * Revision 1.5  1997/01/16 18:41:59  pekangas
  1093.  * Changed copyright messages to Housemarque
  1094.  *
  1095.  * Revision 1.4  1997/01/16 18:25:08  pekangas
  1096.  * Implemented strSetStreamRate, strSetStreamVolume and strSetStreamPanning
  1097.  *
  1098. */
  1099.